#ifndef __NET_TCPSERVER__
#define __NET_TCPSERVER__

#include<iostream>
#include<unordered_map>
#include<mutex>
#include<memory>
#include<functional>

#include"connection.hpp"
#include"acceptor.hpp"
#include"../timer/timer.hpp"
#include"../threadpool/threadpool.hpp"

class TcpServer
{
public:
    using ConnectionCallback=Connection::ConnectionCallback;
    using MessageCallback=Connection::MessageCallback;
    using CloseCallback=Connection::CloseCallback;
    using EveryCallback=Connection::EveryCallback;

public:
    TcpServer(int port)
        :_server_port(port),
         _server_acceptor(std::make_unique<Acceptor>(&_server_accept_loop,port))
    {
        _server_acceptor->set_accpet_callback(std::bind(&TcpServer::new_connection,this,std::placeholders::_1));
        _server_acceptor->Listen();

        _server_threadpool=std::make_unique<Threadpool>(_server_threadpool_num);
        if(_server_threadpool==nullptr)
        {
            ERR("tcp_server create threadpool failed!");
            exit(1);
        }
        INF("tcp_server create threadpool, thread num is %d",_server_threadpool_num);

        _server_message_cb=[this](const ConnectionPtr& conn, InBuffer* inbuf){
                                update_conn_timeout(conn);
                                if(_server_msg_cb)_server_msg_cb(conn,inbuf);
                            };
    }

    void Start()
    {
        if(_server_start)
        {
            ERR("server don't restart");
            return;
        }

        _server_accept_loop.start();

        _server_start=true;
        _server_timer.run();

        for(int i=0;i<_server_threadpool_num;i++)
        {
            _server_connect_loops.emplace_back(new Eventloop());
            Eventloop* ev=_server_connect_loops[i];
            _server_threadpool->post_task([&ev](){ev->start();});
            INF("tcp_server add task to threadpool");
        }
    }

    void set_server_threadpool_num(int num){_server_threadpool_num=num;}

    void set_server_conn_timeout_ms(int time){_server_conn_timeout_ms=time;}

    void set_server_connect_cb(const ConnectionCallback& cb){_server_connect_cb=cb;}

    void set_server_msg_cb(const MessageCallback& cb){_server_msg_cb=cb;}

    void set_server_close_cb(const CloseCallback& cb){_server_close_cb=cb;}

    void set_server_every_cb(const EveryCallback& cb){_server_every_cb=cb;}

private:
    void new_connection(int fd)
    {
        _server_id++;

        Eventloop* sub_loop= get_next_loop();

        ConnectionPtr conn(new Connection(_server_id,fd,sub_loop));
        conn->set_connect_connect_cb(_server_connect_cb);
        conn->set_connect_message_cb(_server_message_cb);
        conn->set_connect_close_cb(_server_close_cb);
        conn->set_connect_server_close_cb(std::bind(&TcpServer::remove_connect,this,std::placeholders::_1));
        
        conn->established();

        auto timer_id=_server_timer.add_node_period(_server_conn_timeout_ms,false,[conn]{
                                                        INF("connection timeout!");
                                                        conn->release();
                                                    });
        conn->set_timer_id(timer_id);

        _server_connections[_server_id]=conn;
    }

    void remove_connect(const ConnectionPtr& conn)
    {
        _server_accept_loop.add_task(std::bind(&TcpServer::remove_connect_in_loop,this,conn));
    }

    void remove_connect_in_loop(const ConnectionPtr& conn)
    {
        int id=conn->get_connid();
        auto it=_server_connections.find(id);
        if(it!=_server_connections.end())
        {
            _server_connections.erase(it);
        }
    }

    Eventloop* get_next_loop() 
    {
        int size=_server_connect_loops.size();
        if(size==0) 
        {
            ERR("get_next_loop failed!");
            return nullptr;
        }

        ++_server_next_loop;
        _server_next_loop = _server_next_loop % size;
        return _server_connect_loops[_server_next_loop]; 
    }

    void update_conn_timeout(const ConnectionPtr& conn)
    {
        _server_timer.cancel_node(conn->get_timer_id());

        auto timer_id=_server_timer.add_node_period(_server_conn_timeout_ms,false,[conn]{
                                                        INF("connection timeout!");
                                                        conn->release();
                                                    });
        conn->set_timer_id(timer_id);
        
        _server_connections[_server_id]=conn;
    }

private:
    int _server_port;

    uint64_t _server_id{0};                        //自然增长的连接ID
    int _server_threadpool_num{1};
    bool _server_start{false};
    int _server_next_loop{-1};
    int _server_conn_timeout_ms{6000};

    Eventloop _server_accept_loop;
    std::vector<Eventloop*> _server_connect_loops;
    std::unique_ptr<Acceptor> _server_acceptor;
    std::unique_ptr<Threadpool> _server_threadpool;
    TimerQueue _server_timer;
    std::unordered_map<uint64_t,ConnectionPtr> _server_connections;

    std::mutex _server_mutex;

    ConnectionCallback _server_connect_cb;
    MessageCallback _server_message_cb;
    MessageCallback _server_msg_cb;
    CloseCallback _server_close_cb;
    EveryCallback _server_every_cb;
};


#endif